<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="JrOFU" id="JrOFU"><span data-lake-id="u1a1f0fa5" id="u1a1f0fa5">典型回答</span></h1>
  <p data-lake-id="ucf2e9c68" id="ucf2e9c68"><br></p>
  <p data-lake-id="u311bb881" id="u311bb881"><span data-lake-id="u2da6bf67" id="u2da6bf67">虽然RR的隔离级别可以在一定程度上避免脏读、不可重复读和幻读等问题，但是，对于很多大型的互联网来说，会愿意将数据库的默认隔离级别调整成并发度更高的RC级别，从而，提升并发度并且降低发生死锁的概率。</span></p>
  <p data-lake-id="u1869deb8" id="u1869deb8"><span data-lake-id="u3d3f882a" id="u3d3f882a">​</span><br></p>
  <h1 data-lake-id="jpyj2" id="jpyj2"><span data-lake-id="u0c859f87" id="u0c859f87">扩展知识</span></h1>
  <p data-lake-id="u8fd9ccb6" id="u8fd9ccb6"><br></p>
  <h2 data-lake-id="HAA45" id="HAA45"><span data-lake-id="u1dabdfc0" id="u1dabdfc0">RR 和 RC 的区别</span></h2>
  <p data-lake-id="u4d787e7e" id="u4d787e7e"><br></p>
  <p data-lake-id="u00f0dbb4" id="u00f0dbb4"><span data-lake-id="u7dc76de7" id="u7dc76de7">我们需要先来弄清楚一下 RR 和 RC 的区别，分析下各自的优缺点。</span></p>
  <p data-lake-id="udc3be3c2" id="udc3be3c2"><br></p>
  <h4 data-lake-id="7f05d4b8" id="7f05d4b8"><span data-lake-id="uf43d894b" id="uf43d894b">一致性读</span></h4>
  <p data-lake-id="u8fba63d9" id="u8fba63d9"><br></p>
  <p data-lake-id="u90dc6003" id="u90dc6003"><span data-lake-id="u67f25a38" id="u67f25a38">一致性读，又称为快照读。快照即当前行数据之前的历史版本。快照读就是使用快照信息显示基于某个时间点的查询结果，而不考虑与此同时运行的其他事务所执行的更改。</span></p>
  <p data-lake-id="ucc418ba4" id="ucc418ba4"><br></p>
  <p data-lake-id="u4f248980" id="u4f248980"><span data-lake-id="u048f747a" id="u048f747a">在MySQL 中，只有READ COMMITTED 和 REPEATABLE READ这两种事务隔离级别才会使用一致性读。</span></p>
  <p data-lake-id="u2c811955" id="u2c811955"><br></p>
  <p data-lake-id="u2832e7ef" id="u2832e7ef"><span data-lake-id="u9d8ac995" id="u9d8ac995">在 RR 中，快照会在事务中第一次SELECT语句执行时生成，只有在本事务中对数据进行更改才会更新快照。</span></p>
  <p data-lake-id="u073c5d29" id="u073c5d29"><br></p>
  <p data-lake-id="ucfbe8982" id="ucfbe8982"><span data-lake-id="u3e639105" id="u3e639105">在 RC 中，每次读取都会重新生成一个快照，总是读取行的最新版本。</span></p>
  <p data-lake-id="u2f34fb16" id="u2f34fb16"><br></p>
  <p data-lake-id="u12a901d4" id="u12a901d4"><span data-lake-id="u6bb8e0c3" id="u6bb8e0c3">在数据库的 </span><strong><span data-lake-id="uf1b48b25" id="uf1b48b25">RC 这种隔离级别中，还支持"半一致读"</span></strong><span data-lake-id="ub3bc09bf" id="ub3bc09bf"> ，一条update语句，如果 where 条件匹配到的记录已经加锁，那么InnoDB会返回记录最近提交的版本，由MySQL上层判断此是否需要真的加锁。</span></p>
  <p data-lake-id="u627ba994" id="u627ba994"><br></p>
  <h4 data-lake-id="c812c111" id="c812c111"><span data-lake-id="u3d01b5fe" id="u3d01b5fe">锁机制</span></h4>
  <p data-lake-id="u80d0ef1f" id="u80d0ef1f"><br></p>
  <p data-lake-id="u6e148ede" id="u6e148ede"><span data-lake-id="u02e40f6a" id="u02e40f6a">数据库的锁，在不同的事务隔离级别下，是采用了不同的机制的。在 MySQL 中，有三种类型的锁，分别是Record Lock、Gap Lock和 Next-Key Lock。</span></p>
  <p data-lake-id="uf8d98ae1" id="uf8d98ae1"><br></p>
  <blockquote data-lake-id="u862c686e" id="u862c686e">
   <p data-lake-id="uaa7085da" id="uaa7085da"><span data-lake-id="ub8c26c77" id="ub8c26c77">Record Lock表示记录锁，锁的是索引记录。</span></p>
   <p data-lake-id="uce6ced61" id="uce6ced61"><span data-lake-id="u34c73728" id="u34c73728"> </span></p>
   <p data-lake-id="u0125ce53" id="u0125ce53"><span data-lake-id="u2b9d5323" id="u2b9d5323">Gap Lock是间隙锁，锁的是索引记录之间的间隙。</span></p>
   <p data-lake-id="u7a218837" id="u7a218837"><span data-lake-id="u18218236" id="u18218236"> </span></p>
   <p data-lake-id="ud061244a" id="ud061244a"><span data-lake-id="u412e93ae" id="u412e93ae">Next-Key Lock是Record Lock和Gap Lock的组合，同时锁索引记录和间隙。他的范围是左开右闭的。</span></p>
  </blockquote>
  <p data-lake-id="u244e5be4" id="u244e5be4"><br></p>
  <p data-lake-id="u16bf3fed" id="u16bf3fed"><span data-lake-id="ucd792c0b" id="ucd792c0b">在 RC 中，只会对索引增加Record Lock，不会添加Gap Lock和Next-Key Lock。</span></p>
  <p data-lake-id="uadc9da48" id="uadc9da48"><br></p>
  <p data-lake-id="ua17ddd75" id="ua17ddd75"><span data-lake-id="u78ab7790" id="u78ab7790">在 RR 中，为了解决幻读的问题，在支持Record Lock的同时，还支持Gap Lock和Next-Key Lock；</span></p>
  <p data-lake-id="u1e7aa4d6" id="u1e7aa4d6"><br></p>
  <h4 data-lake-id="449d84a4" id="449d84a4"><span data-lake-id="ucd6bb6f1" id="ucd6bb6f1">主从同步</span></h4>
  <p data-lake-id="u2b09855b" id="u2b09855b"><br></p>
  <p data-lake-id="ue50938a3" id="ue50938a3"><span data-lake-id="u71687ee3" id="u71687ee3">在数据主从同步时，不同格式的 binlog 也对事务隔离级别有要求。</span></p>
  <p data-lake-id="u042fbf09" id="u042fbf09"><br></p>
  <p data-lake-id="u57554641" id="u57554641"><span data-lake-id="ueb121769" id="ueb121769">MySQL的binlog主要支持三种格式，分别是statement、row以及mixed，但是，RC 隔离级别只支持row格式的binlog。如果指定了mixed作为 binlog 格式，那么如果使用RC，服务器会自动使用基于row 格式的日志记录。</span></p>
  <p data-lake-id="u930a055c" id="u930a055c"><br></p>
  <p data-lake-id="u7eefa406" id="u7eefa406"><span data-lake-id="u425f3ad1" id="u425f3ad1">而 RR 的隔离级别同时支持statement、row以及mixed三种。</span></p>
  <p data-lake-id="ua4f6095b" id="ua4f6095b"><br></p>
  <h3 data-lake-id="e550c11f" id="e550c11f"><span data-lake-id="u010db7c2" id="u010db7c2">为什么互联网公司选择使用 RC</span></h3>
  <p data-lake-id="uc8f0abe7" id="uc8f0abe7"><br></p>
  <h4 data-lake-id="89cc75de" id="89cc75de"><span data-lake-id="uaf1fc8b8" id="uaf1fc8b8">提升并发</span></h4>
  <p data-lake-id="u84de6600" id="u84de6600"><br></p>
  <p data-lake-id="u1560dc00" id="u1560dc00"><span data-lake-id="u1ebc67f5" id="u1ebc67f5">互联网公司和传统企业最大的区别是什么？</span></p>
  <p data-lake-id="ua259d5bb" id="ua259d5bb"><br></p>
  <p data-lake-id="u6920483d" id="u6920483d"><span data-lake-id="u68f5f71e" id="u68f5f71e">高并发！</span></p>
  <p data-lake-id="u2590ad27" id="u2590ad27"><br></p>
  <p data-lake-id="u21db1465" id="u21db1465"><span data-lake-id="u2adda0c0" id="u2adda0c0">没错，互联网业务的并发度比传统企业要高处很多。2020年双十一当天，订单创建峰值达到 58.3 万笔/秒。</span></p>
  <p data-lake-id="u74459891" id="u74459891"><br></p>
  <p data-lake-id="u3c8af037" id="u3c8af037"><span data-lake-id="ubc1ade13" id="ubc1ade13">很多人问，要怎么做才能扛得住这么大的并发量。其实，这背后的优化多到几个小时都讲不完，因为要做的、可以做的事情实在是太多了。</span></p>
  <p data-lake-id="ua68efb6c" id="ua68efb6c"><br></p>
  <p data-lake-id="ub1f354d6" id="ub1f354d6"><span data-lake-id="ud00a5693" id="ud00a5693">而有一个和我们今天这篇文章有关的优化，那就是通过修改数据库的隔离级别来提升并发度。</span></p>
  <p data-lake-id="u0e5d3815" id="u0e5d3815"><br></p>
  <p data-lake-id="u777bf6a0" id="u777bf6a0"><span data-lake-id="ub4322841" id="ub4322841">为什么 RC 比 RR 的并发度要好呢？</span></p>
  <p data-lake-id="uf6123132" id="uf6123132"><br></p>
  <p data-lake-id="u49f1c5b3" id="u49f1c5b3"><span data-lake-id="u664b6719" id="u664b6719">首先，RC 在加锁的过程中，是不需要添加Gap Lock和 Next-Key Lock 的，只对要修改的记录添加行级锁就行了。</span></p>
  <p data-lake-id="u974f48ee" id="u974f48ee"><br></p>
  <p data-lake-id="ucda818a5" id="ucda818a5"><span data-lake-id="u91da9f9d" id="u91da9f9d">这就使得并发度要比 RR 高很多。</span></p>
  <p data-lake-id="ue0a79dbb" id="ue0a79dbb"><br></p>
  <p data-lake-id="u3db0ba9d" id="u3db0ba9d"><span data-lake-id="u1f6a27c2" id="u1f6a27c2">另外，因为 RC 还支持"半一致读"，可以大大的减少了更新语句时行锁的冲突；对于不满足更新条件的记录，可以提前释放锁，提升并发度。</span></p>
  <p data-lake-id="u16d9b753" id="u16d9b753"><br></p>
  <h4 data-lake-id="4308edc4" id="4308edc4"><span data-lake-id="u611b030d" id="u611b030d">减少死锁</span></h4>
  <p data-lake-id="u487bb85b" id="u487bb85b"><br></p>
  <p data-lake-id="uf428a51b" id="uf428a51b"><span data-lake-id="u8034034f" id="u8034034f">因为RR这种事务隔离级别会增加Gap Lock和 Next-Key Lock，这就使得锁的粒度变大，那么就会使得死锁的概率增大。</span></p>
  <p data-lake-id="u468ae1bf" id="u468ae1bf"><br></p>
  <blockquote data-lake-id="u33d519f6" id="u33d519f6">
   <p data-lake-id="uefe10b01" id="uefe10b01"><span data-lake-id="u40d42f70" id="u40d42f70">死锁：一个事务锁住了表A，然后又访问表B；另一个事务锁住了表B，然后企图访问表A；这时就会互相等待对方释放锁，就导致了死锁。</span></p>
  </blockquote>
  <p data-lake-id="u1c2b9418" id="u1c2b9418"><br></p>
  <p data-lake-id="uf80824d1" id="uf80824d1"><span data-lake-id="u86c0bee5" id="u86c0bee5">RR和RC主要在加锁机制、主从同步以及一致性读方面存在一些差异。</span></p>
  <p data-lake-id="ue24a090e" id="ue24a090e"><br></p>
  <p data-lake-id="u9cd5ff81" id="u9cd5ff81"><span data-lake-id="u8b030f63" id="u8b030f63">而很多大厂，为了提升并发度和降低死锁发生的概率，会把数据库的隔离级别从默认的 RR 调整成 RC。</span></p>
  <p data-lake-id="ucddea989" id="ucddea989"><br></p>
  <p data-lake-id="u9f775b65" id="u9f775b65"><span data-lake-id="u54943192" id="u54943192">当然，这样做也不是完全没有问题，首先使用 RC 之后，就需要自己解决不可重复读的问题，这个其实还好，很多时候不可重复读问题其实是可以忽略的，或者可以用其他手段解决。</span></p>
  <p data-lake-id="ue108d08c" id="ue108d08c"><span data-lake-id="u31c37832" id="u31c37832">​</span><br></p>
  <p data-lake-id="uc0e128b9" id="uc0e128b9"><span data-lake-id="u6d09af29" id="u6d09af29">比如读取到别的事务修改的值其实问题不太大的，只要修改的时候的不基于错误数据就可以了，所以我们都是在核心表中增加乐观锁标记，更新的时候都要带上锁标记进行乐观锁更新。</span></p>
  <p data-lake-id="u2e8de6a6" id="u2e8de6a6"><br></p>
  <p data-lake-id="u23b280de" id="u23b280de"><span data-lake-id="u31cb0746" id="u31cb0746">还有就是使用 RC 的时候，不能使用statement格式的 binlog，这种影响其实可以忽略不计了，因为MySQL是在5.1.5版本开始支持row的、在5.1.8版本中开始支持mixed，后面这两种可以代替 statement格式。</span></p>
  <p data-lake-id="u3a7f9680" id="u3a7f9680"><br></p>
  <p data-lake-id="u066a8bed" id="u066a8bed"><strong><span data-lake-id="uf2c9c5b4" id="uf2c9c5b4">所有的技术方案的选择，都是一种权衡的艺术！</span></strong></p>
 </body>
</html>